// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: lucasyoo

// #include "coin2/strategy/vmm/vmm.h"
#include "coin2/strategy/vmm/vmm_order_controller2.h"

#include <gtest/gtest.h>
#include <nlohmann/json.hpp>

#include "coin2/strategy/order_executor/layering_executor3.h"
#include "coin2/strategy/vmm/vmm_factory.h"

namespace coin2::strategy::vmm {
using IProduct = coin2::exchange::base::symbology::IProduct;
using coin2::exchange::base::symbology::ProductEncyclopedia;
using coin2::strategy::order_executor::LayeringExecutor3;
using OrderSpec = coin2::exchange::base::order::OrderSpec;
using OrderContext = coin2::exchange::base::order::OrderContext;
using coin::proto::OrderDuration;
using coin::proto::OrderEvent;
using coin::proto::OrderSide;
using coin::proto::OrderType;
using coin::proto::TradeSide;

// const std::string symbol = "XRP-USDT";
// const std::string mea_str = "Spot.Huobi.v1";
// const std::string tag = "test";

class PseudoController : public coin2::strategy::vmm::VmmDeltaHedgeOrderController {
 public:
  PseudoController(const ::nlohmann::json& config, bool is_sim)
      : VmmDeltaHedgeOrderController(config, is_sim) {}
  void SetBuyPq(const std::string& symbol, const std::vector<std::pair<double, double>> pq) {
    last_buy_pq_[symbol] = pq;
  }
  void SetSellPq(const std::string& symbol, std::vector<std::pair<double, double>> pq) {
    last_sell_pq_[symbol] = pq;
  }
  void SetDecoBuyPq(const std::string& symbol, const std::deque<std::pair<double, double>> pq) {
    last_deco_buy_pq_[symbol] = pq;
  }
  void SetDecoSellPq(const std::string& symbol, std::deque<std::pair<double, double>> pq) {
    last_deco_sell_pq_[symbol] = pq;
  }
  std::vector<std::pair<double, double>> BuyPq(const std::string& symbol) {
    return last_buy_pq_[symbol];
  }
  std::vector<std::pair<double, double>> SellPq(const std::string& symbol) {
    return last_sell_pq_[symbol];
  }
  std::deque<std::pair<double, double>> DecoBuyPq(const std::string& symbol) {
    return last_deco_buy_pq_[symbol];
  }
  std::deque<std::pair<double, double>> DecoSellPq(const std::string& symbol) {
    return last_deco_sell_pq_[symbol];
  }
};

class VmmDeltaHedgeOrderControllerTest : public ::testing::Test {
 protected:
  void SetUp() override {
    std::experimental::filesystem::path file_path =
        fmt::format("{}/cc/testcoin2/strategy/vmm/vmm_test.json", std::getenv("COIN_REPO"));
    std::ifstream in(file_path);
    auto total_config = ::nlohmann::json::parse(in);
    for (const auto& exe_config : total_config["executors"].items()) {
      PassiveExecutorConfig config;
      const auto& symbol = exe_config.key();
      symbols_.push_back(symbol);
      CHECK(JsonStringToMessage(exe_config.value().dump(), &config).ok());
      total_config["oc_config"]["configs"][symbol]["min_pos"] = config.min_pos();
      total_config["oc_config"]["configs"][symbol]["max_pos"] = config.max_pos();
      total_config["oc_config"]["configs"][symbol]["lot_size"] = config.lot_size();
      total_config["oc_config"]["configs"][symbol]["reserve"] = config.reserve();
    }
    controller_ = std::make_unique<PseudoController>(total_config["oc_config"]["configs"], true);
  }
  std::unique_ptr<PseudoController> controller_;
  std::vector<std::string> symbols_;
};


// BidConcat1: Decorator without duplicates
TEST_F(VmmDeltaHedgeOrderControllerTest, BidConcat1) {
  const auto& symbol = symbols_[0];
  const auto example_orders =
      std::vector<std::pair<double, double>>{{0.300, 1000.0}, {0.299, 2000.0}, {0.298, 3000.0}};
  const auto example_decorators =
      std::deque<std::pair<double, double>>{{0.295, 1000.0}, {0.294, 2000.0}, {0.293, 3000.0}};
  controller_->SetBuyPq(symbol, example_orders);
  controller_->SetDecoBuyPq(symbol, example_decorators);
  controller_->ConcatDecoOrders(1, symbol);

  const auto expected_res = std::vector<std::pair<double, double>>{{0.300, 1000.0},
                                                                   {0.299, 2000.0},
                                                                   {0.298, 3000.0},
                                                                   {0.295, 1000.0},
                                                                   {0.294, 2000.0},
                                                                   {0.293, 3000.0}};
  const auto res = controller_->BuyPq(symbol);
  ASSERT_EQ(expected_res, res);
}

// BidConcat2: Decorator without duplicates but mixed prices
TEST_F(VmmDeltaHedgeOrderControllerTest, BidConcat2) {
  const auto& symbol = symbols_[0];
  const auto example_orders =
      std::vector<std::pair<double, double>>{{0.300, 1000.0}, {0.299, 2000.0}, {0.295, 3000.0}};
  const auto example_decorators =
      std::deque<std::pair<double, double>>{{0.298, 1000.0}, {0.297, 2000.0}, {0.293, 3000.0}};
  controller_->SetBuyPq(symbol, example_orders);
  controller_->SetDecoBuyPq(symbol, example_decorators);
  controller_->ConcatDecoOrders(1, symbol);

  const auto expected_res = std::vector<std::pair<double, double>>{{0.300, 1000.0},
                                                                   {0.299, 2000.0},
                                                                   {0.298, 1000.0},
                                                                   {0.297, 2000.0},
                                                                   {0.295, 3000.0},
                                                                   {0.293, 3000.0}};
  const auto res = controller_->BuyPq(symbol);
  ASSERT_EQ(expected_res, res);
}


// BidConcat3: Decorator with one duplicates
TEST_F(VmmDeltaHedgeOrderControllerTest, BidConcat3) {
  const auto& symbol = symbols_[0];
  const auto example_orders =
      std::vector<std::pair<double, double>>{{0.300, 1000.0}, {0.299, 2000.0}, {0.298, 3000.0}};
  const auto example_decorators =
      std::deque<std::pair<double, double>>{{0.298, 1000.0}, {0.294, 2000.0}, {0.293, 3000.0}};
  controller_->SetBuyPq(symbol, example_orders);
  controller_->SetDecoBuyPq(symbol, example_decorators);
  controller_->ConcatDecoOrders(1, symbol);

  const auto expected_res = std::vector<std::pair<double, double>>{{0.300, 1000.0},
                                                                   {0.299, 2000.0},
                                                                   {0.298, 4000.0},
                                                                   {0.294, 2000.0},
                                                                   {0.293, 3000.0}};
  const auto res = controller_->BuyPq(symbol);
  ASSERT_EQ(expected_res, res);
}


// BidConcat4: Decorator with more duplicates
TEST_F(VmmDeltaHedgeOrderControllerTest, BidConcat4) {
  const auto& symbol = symbols_[0];
  const auto example_orders =
      std::vector<std::pair<double, double>>{{0.300, 1000.0}, {0.299, 2000.0}, {0.298, 3000.0}};
  const auto example_decorators =
      std::deque<std::pair<double, double>>{{0.299, 1000.0}, {0.298, 2000.0}, {0.297, 3000.0}};
  controller_->SetBuyPq(symbol, example_orders);
  controller_->SetDecoBuyPq(symbol, example_decorators);
  controller_->ConcatDecoOrders(1, symbol);

  const auto expected_res = std::vector<std::pair<double, double>>{{0.300, 1000.0},
                                                                   {0.299, 3000.0},
                                                                   {0.298, 5000.0},
                                                                   {0.297, 3000.0}};
  const auto res = controller_->BuyPq(symbol);
  ASSERT_EQ(expected_res, res);
}

// BidConcat5: Decorator with more duplicates and mixed prices
TEST_F(VmmDeltaHedgeOrderControllerTest, BidConcat5) {
  const auto& symbol = symbols_[0];
  const auto example_orders =
      std::vector<std::pair<double, double>>{{0.300, 1000.0}, {0.299, 2000.0}, {0.298, 3000.0}};
  const auto example_decorators =
      std::deque<std::pair<double, double>>{{0.300, 1000.0}, {0.298, 2000.0}, {0.297, 3000.0}};
  controller_->SetBuyPq(symbol, example_orders);
  controller_->SetDecoBuyPq(symbol, example_decorators);
  controller_->ConcatDecoOrders(1, symbol);

  const auto expected_res = std::vector<std::pair<double, double>>{{0.300, 2000.0},
                                                                   {0.299, 2000.0},
                                                                   {0.298, 5000.0},
                                                                   {0.297, 3000.0}};
  const auto res = controller_->BuyPq(symbol);
  ASSERT_EQ(expected_res, res);
}


// AskConcat1: Decorator without duplicates
TEST_F(VmmDeltaHedgeOrderControllerTest, AskConcat1) {
  const auto& symbol = symbols_[0];
  const auto example_orders =
      std::vector<std::pair<double, double>>{{0.300, 1000.0}, {0.301, 2000.0}, {0.302, 3000.0}};
  const auto example_decorators =
      std::deque<std::pair<double, double>>{{0.305, 1000.0}, {0.306, 2000.0}, {0.307, 3000.0}};
  controller_->SetSellPq(symbol, example_orders);
  controller_->SetDecoSellPq(symbol, example_decorators);
  controller_->ConcatDecoOrders(-1, symbol);

  const auto expected_res = std::vector<std::pair<double, double>>{{0.300, 1000.0},
                                                                   {0.301, 2000.0},
                                                                   {0.302, 3000.0},
                                                                   {0.305, 1000.0},
                                                                   {0.306, 2000.0},
                                                                   {0.307, 3000.0}};
  const auto res = controller_->SellPq(symbol);
  ASSERT_EQ(expected_res, res);
}

// AskConcat2: Decorator without duplicates but mixed prices
TEST_F(VmmDeltaHedgeOrderControllerTest, AskConcat2) {
  const auto& symbol = symbols_[0];
  const auto example_orders =
      std::vector<std::pair<double, double>>{{0.300, 1000.0}, {0.301, 2000.0}, {0.305, 3000.0}};
  const auto example_decorators =
      std::deque<std::pair<double, double>>{{0.302, 1000.0}, {0.304, 2000.0}, {0.308, 3000.0}};
  controller_->SetSellPq(symbol, example_orders);
  controller_->SetDecoSellPq(symbol, example_decorators);
  controller_->ConcatDecoOrders(-1, symbol);

  const auto expected_res = std::vector<std::pair<double, double>>{{0.300, 1000.0},
                                                                   {0.301, 2000.0},
                                                                   {0.302, 1000.0},
                                                                   {0.304, 2000.0},
                                                                   {0.305, 3000.0},
                                                                   {0.308, 3000.0}};
  const auto res = controller_->SellPq(symbol);
  ASSERT_EQ(expected_res, res);
}


// AskConcat3: Decorator with one duplicates
TEST_F(VmmDeltaHedgeOrderControllerTest, AskConcat3) {
  const auto& symbol = symbols_[0];
  const auto example_orders =
      std::vector<std::pair<double, double>>{{0.300, 1000.0}, {0.301, 2000.0}, {0.302, 3000.0}};
  const auto example_decorators =
      std::deque<std::pair<double, double>>{{0.302, 1000.0}, {0.307, 2000.0}, {0.310, 3000.0}};
  controller_->SetSellPq(symbol, example_orders);
  controller_->SetDecoSellPq(symbol, example_decorators);
  controller_->ConcatDecoOrders(-1, symbol);


  const auto expected_res = std::vector<std::pair<double, double>>{{0.300, 1000.0},
                                                                   {0.301, 2000.0},
                                                                   {0.302, 4000.0},
                                                                   {0.307, 2000.0},
                                                                   {0.310, 3000.0}};
  const auto res = controller_->SellPq(symbol);
  ASSERT_EQ(expected_res, res);
}


// AskConcat4: Decorator with more duplicates
TEST_F(VmmDeltaHedgeOrderControllerTest, AskConcat4) {
  const auto& symbol = symbols_[0];
  const auto example_orders =
      std::vector<std::pair<double, double>>{{0.300, 1000.0}, {0.301, 2000.0}, {0.302, 3000.0}};
  const auto example_decorators =
      std::deque<std::pair<double, double>>{{0.301, 1000.0}, {0.302, 2000.0}, {0.303, 3000.0}};
  controller_->SetSellPq(symbol, example_orders);
  controller_->SetDecoSellPq(symbol, example_decorators);
  controller_->ConcatDecoOrders(-1, symbol);

  const auto expected_res = std::vector<std::pair<double, double>>{{0.300, 1000.0},
                                                                   {0.301, 3000.0},
                                                                   {0.302, 5000.0},
                                                                   {0.303, 3000.0}};
  const auto res = controller_->SellPq(symbol);
  ASSERT_EQ(expected_res, res);
}


// AskConcat4: Decorator with more duplicates
TEST_F(VmmDeltaHedgeOrderControllerTest, AskConcat5) {
  const auto& symbol = symbols_[0];
  const auto example_orders =
      std::vector<std::pair<double, double>>{{0.300, 1000.0}, {0.301, 2000.0}, {0.302, 3000.0}};
  const auto example_decorators =
      std::deque<std::pair<double, double>>{{0.300, 1000.0}, {0.302, 2000.0}, {0.303, 3000.0}};
  controller_->SetSellPq(symbol, example_orders);
  controller_->SetDecoSellPq(symbol, example_decorators);
  controller_->ConcatDecoOrders(-1, symbol);

  const auto expected_res = std::vector<std::pair<double, double>>{{0.300, 2000.0},
                                                                   {0.301, 2000.0},
                                                                   {0.302, 5000.0},
                                                                   {0.303, 3000.0}};
  const auto res = controller_->SellPq(symbol);
  ASSERT_EQ(expected_res, res);
}


}  // namespace coin2::strategy::vmm
